package client

import (
	"fmt"
	"gopkg.in/yaml.v1"
	"io/ioutil"
	"net"
	"net/url"
	"os"
	"path"
	"regexp"
	"strconv"
	"strings"
	"tikrok/internal/log"
)

type Configuration struct {
	HttpProxy          string                          `yaml:"http_proxy,omitempty"`
	ServerAddr         string                          `yaml:"server_addr,omitempty"`
	InspectAddr        string                          `yaml:"inspect_addr,omitempty"`
	TrustHostRootCerts bool                            `yaml:"trust_host_root_certs,omitempty"`
	AuthToken          string                          `yaml:"auth_token,omitempty"`
	Tunnels            map[string]*TunnelConfiguration `yaml:"tunnels,omitempty"`
	LogTo              string                          `yaml:"-"`
	Path               string                          `yaml:"-"`
}

type TunnelConfiguration struct {
	Subdomain  string            `yaml:"subdomain,omitempty"`
	Hostname   string            `yaml:"hostname,omitempty"`
	Protocols  map[string]string `yaml:"proto,omitempty"`
	HttpAuth   string            `yaml:"auth,omitempty"`
	RemotePort uint16            `yaml:"remote_port,omitempty"`
}

func LoadConfiguration(opts *Options) (config *Configuration, err error) {
	configPath := opts.config
	if configPath == "" {
		configPath = defaultPath()
	}

	log.Info("Reading configuration file %s", configPath)
	configBuf, err := os.ReadFile(configPath)
	defaultConfigBuf, err := os.ReadFile(defaultPath())
	if err != nil {
		// failure to read a configuration file is only a fatal error if
		// the user specified one explicitly
		if opts.config != "" {
			err = fmt.Errorf("Failed to read configuration file %s: %v", configPath, err)
			// 未指定配置文件时继续使用默认配置
			configBuf = []byte{}
			return
		}
	}

	//config = new(Configuration)
	//if err = yaml.Unmarshal(configBuf, &config); err != nil {
	//	err = fmt.Errorf("Error parsing configuration file %s: %v", configPath, err)
	//	return
	//}
	// try to parse the old .ngrok format for backwards compatibility
	//matched := false
	//content := strings.TrimSpace(string(configBuf))
	//if matched, err = regexp.MatchString("^[0-9a-zA-Z_\\-!]+$", content); err != nil {
	//	return
	//} else if matched {
	//	config = &Configuration{AuthToken: content}
	//}

	// deserialize/parse the config
	config = new(Configuration)

	if len(configBuf) > 0 {
		if err = yaml.Unmarshal(configBuf, config); err != nil {
			// 尝试旧格式处理
			content := strings.TrimSpace(string(configBuf))
			if matched, _ := regexp.MatchString("^[0-9a-zA-Z_\\-!]+$", content); matched {
				config = &Configuration{AuthToken: content}
			} else {
				return nil, fmt.Errorf("error parsing configuration file %s: %w", configPath, err)
			}
		}
	}

	if len(defaultConfigBuf) > 0 {
		if err = yaml.Unmarshal(defaultConfigBuf, config); err == nil {
			// 成功解析YAML格式
			log.Debug("Successfully parsed YAML configuration")
		} else {
			// 尝试旧格式处理（纯token字符串）
			content := strings.TrimSpace(string(defaultConfigBuf))
			if matched, _ := regexp.MatchString("^[0-9a-zA-Z_\\-!]+$", content); matched {
				log.Info("Migrating legacy config format")
				config = &Configuration{AuthToken: content}
			} else {
				return nil, fmt.Errorf("error parsing configuration file %s: %w", configPath, err)
			}
		}
	}

	// set configuration defaults
	if config.ServerAddr == "" {
		config.ServerAddr = defaultServerAddr
	}

	if config.InspectAddr == "" {
		config.InspectAddr = defaultInspectAddr
	}

	if config.HttpProxy == "" {
		config.HttpProxy = os.Getenv("http_proxy")
	}

	// validate and normalize configuration
	if config.InspectAddr != "disabled" {
		if config.InspectAddr, err = normalizeAddress(config.InspectAddr, "inspect_addr"); err != nil {
			return
		}
	}

	if config.ServerAddr, err = normalizeAddress(config.ServerAddr, "server_addr"); err != nil {
		return
	}

	if config.HttpProxy != "" {
		var proxyUrl *url.URL
		if proxyUrl, err = url.Parse(config.HttpProxy); err != nil {
			return
		} else {
			if proxyUrl.Scheme != "http" && proxyUrl.Scheme != "https" {
				err = fmt.Errorf("Proxy url scheme must be 'http' or 'https', got %v", proxyUrl.Scheme)
				return
			}
		}
	}

	for name, t := range config.Tunnels {
		if t == nil || t.Protocols == nil || len(t.Protocols) == 0 {
			err = fmt.Errorf("Tunnel %s does not specify any protocols to tunnel.", name)
			return
		}

		for k, addr := range t.Protocols {
			tunnelName := fmt.Sprintf("for tunnel %s[%s]", name, k)
			if t.Protocols[k], err = normalizeAddress(addr, tunnelName); err != nil {
				return
			}

			if err = validateProtocol(k, tunnelName); err != nil {
				return
			}
		}

		// use the name of the tunnel as the subdomain if none is specified
		if t.Hostname == "" && t.Subdomain == "" {
			// XXX: a crude heuristic, really we should be checking if the last part
			// is a TLD
			if len(strings.Split(name, ".")) > 1 {
				t.Hostname = name
			} else {
				t.Subdomain = name
			}
		}
	}

	// override configuration with command-line options
	config.LogTo = opts.logto
	config.Path = configPath
	if opts.authtoken != "" {
		config.AuthToken = opts.authtoken
	}

	switch opts.command {
	// start a single tunnel, the default, simple ngrok behavior
	case "default":
		config.Tunnels = make(map[string]*TunnelConfiguration)
		config.Tunnels["default"] = &TunnelConfiguration{
			Subdomain: opts.subdomain,
			Hostname:  opts.hostname,
			HttpAuth:  opts.httpauth,
			Protocols: make(map[string]string),
		}

		for _, proto := range strings.Split(opts.protocol, "+") {
			if err = validateProtocol(proto, "default"); err != nil {
				return
			}

			if config.Tunnels["default"].Protocols[proto], err = normalizeAddress(opts.args[0], ""); err != nil {
				return
			}
		}

	// list tunnels
	case "list":
		for name, _ := range config.Tunnels {
			fmt.Println(name)
		}
		os.Exit(0)

	// start tunnels
	case "start":
		if len(opts.args) == 0 {
			err = fmt.Errorf("You must specify at least one tunnel to start")
			return
		}

		requestedTunnels := make(map[string]bool)
		for _, arg := range opts.args {
			requestedTunnels[arg] = true

			if _, ok := config.Tunnels[arg]; !ok {
				err = fmt.Errorf("Requested to start tunnel %s which is not defined in the config file.", arg)
				return
			}
		}

		for name, _ := range config.Tunnels {
			if !requestedTunnels[name] {
				delete(config.Tunnels, name)
			}
		}

	case "start-all":
		return

	default:
		err = fmt.Errorf("Unknown command: %s", opts.command)
		return
	}

	return
}

//	func defaultPath() string {
//		user, err := user.Current()
//
//		// user.Current() does not work on linux when cross compiling because
//		// it requires CGO; use os.Getenv("HOME") hack until we compile natively
//		homeDir, err := os.UserHomeDir()
//		if err != nil {
//			log.Warn("Failed to get user's home directory: %s. Using $HOME: %s", err.Error(), homeDir)
//		} else {
//			homeDir = user.HomeDir
//		}
//
//		return path.Join(homeDir, ".tikrok")
//	}
func defaultPath() string {
	homeDir, err := os.UserHomeDir()
	if err != nil {
		log.Warn("Failed to get user's home directory: %v", err)
		// 否则使用当前目录，但可能永远不会执行
		return path.Join("./")
	}
	return path.Join(homeDir, ".tikrok")
}

func normalizeAddress(addr string, propName string) (string, error) {
	// normalize port to address
	if _, err := strconv.Atoi(addr); err == nil {
		addr = ":" + addr
	}

	host, port, err := net.SplitHostPort(addr)
	if err != nil {
		return "", fmt.Errorf("Invalid address %s '%s': %s", propName, addr, err.Error())
	}

	if host == "" {
		host = "127.0.0.1"
	}

	return fmt.Sprintf("%s:%s", host, port), nil
}

func validateProtocol(proto, propName string) (err error) {
	switch proto {
	case "http", "https", "http+https", "tcp":
	default:
		err = fmt.Errorf("Invalid protocol for %s: %s", propName, proto)
	}

	return
}

func SaveAuthToken(configPath, authtoken string) (err error) {
	// empty configuration by default for the case that we can't read it
	c := new(Configuration)

	// read the configuration
	oldConfigBytes, err := ioutil.ReadFile(configPath)
	if err == nil {
		// unmarshal if we successfully read the configuration file
		if err = yaml.Unmarshal(oldConfigBytes, c); err != nil {
			return
		}
	}

	// no need to save, the authtoken is already the correct value
	if c.AuthToken == authtoken {
		return
	}

	// update auth token
	c.AuthToken = authtoken

	// rewrite configuration
	newConfigBytes, err := yaml.Marshal(c)
	if err != nil {
		return
	}

	err = ioutil.WriteFile(configPath, newConfigBytes, 0600)
	return
}

//
//package client
//
//import (
//	"errors"
//	"fmt"
//	"gopkg.in/yaml.v1"
//	"net"
//	"net/url"
//	"os"
//	"path"
//	"regexp"
//	"strconv"
//	"strings"
//	"sync"
//
//	//"gopkg.in/yaml.v3"
//	"tikrok/internal/log"
//)
//
//const (
//	//defaultServerAddr  = "ngrok.com:443"
//	//defaultInspectAddr = "localhost:4040"
//	configVersion = "v3"
//)
//
//type Configuration struct {
//	sync.RWMutex   `yaml:"-"`
//	Version        string                          `yaml:"version"`
//	HttpProxy      string                          `yaml:"http_proxy,omitempty"`
//	ServerAddr     string                          `yaml:"server_addr,omitempty"`
//	InspectAddr    string                          `yaml:"inspect_addr,omitempty"`
//	TrustHostCerts bool                            `yaml:"trust_host_certs,omitempty"`
//	AuthToken      string                          `yaml:"auth_token,omitempty"`
//	Tunnels        map[string]*TunnelConfiguration `yaml:"tunnels,omitempty"`
//	LogTo          string                          `yaml:"-"`
//	Path           string                          `yaml:"-"`
//}
//
//type TunnelConfiguration struct {
//	Subdomain  string            `yaml:"subdomain,omitempty"`
//	Hostname   string            `yaml:"hostname,omitempty"`
//	Protocols  map[string]string `yaml:"proto,omitempty"`
//	HttpAuth   string            `yaml:"auth,omitempty"`
//	RemotePort int               `yaml:"remote_port,omitempty"`
//}
//
//func LoadConfiguration(opts *Options) (*Configuration, error) {
//	configPath := opts.config
//	if configPath == "" {
//		configPath = defaultPath()
//	}
//
//	log.Debug("Loading configuration from: %s", configPath)
//	configBuf, err := os.ReadFile(configPath)
//	if err != nil {
//		if opts.config != "" {
//			return nil, fmt.Errorf("failed to read config: %w", err)
//		}
//		log.Info("Using default configuration")
//	}
//
//	config := &Configuration{
//		Version:    configVersion,
//		Tunnels:    make(map[string]*TunnelConfiguration),
//		ServerAddr: defaultServerAddr,
//		LogTo:      opts.logto,
//		Path:       configPath,
//	}
//
//	if len(configBuf) > 0 {
//		if err = yaml.Unmarshal(configBuf, config); err != nil {
//			if isLegacyFormat(configBuf) {
//				log.Info("Migrating legacy config format")
//				config = &Configuration{
//					Version:    configVersion,
//					AuthToken:  strings.TrimSpace(string(configBuf)),
//					ServerAddr: defaultServerAddr,
//				}
//			} else {
//				return nil, fmt.Errorf("config parse error: %w", err)
//			}
//		}
//	}
//
//	applyDefaults(config)
//	applyEnvOverrides(config)
//	applyCommandLineOptions(config, opts)
//
//	if err := config.Validate(); err != nil {
//		return nil, fmt.Errorf("config validation failed: %w", err)
//	}
//
//	return config, nil
//}
//
//func isLegacyFormat(data []byte) bool {
//	content := strings.TrimSpace(string(data))
//	matched, _ := regexp.MatchString(`^[0-9a-zA-Z_\-!]+$`, content)
//	return matched
//}
//
//func applyDefaults(c *Configuration) {
//	if c.InspectAddr == "" {
//		c.InspectAddr = defaultInspectAddr
//	}
//
//	if c.HttpProxy == "" {
//		c.HttpProxy = os.Getenv("HTTP_PROXY")
//	}
//
//	for name, t := range c.Tunnels {
//		if t.Hostname == "" && t.Subdomain == "" {
//			if isDomainName(name) {
//				t.Hostname = name
//			} else {
//				t.Subdomain = name
//			}
//		}
//	}
//}
//
//func applyEnvOverrides(c *Configuration) {
//	if auth := os.Getenv("TIKROK_AUTH_TOKEN"); auth != "" {
//		c.AuthToken = auth
//	}
//}
//
//func applyCommandLineOptions(c *Configuration, opts *Options) {
//	if opts.authtoken != "" {
//		c.AuthToken = opts.authtoken
//	}
//
//	switch opts.command {
//	case "default":
//		if existing, ok := c.Tunnels["default"]; ok {
//			mergeTunnelConfig(existing, opts)
//		} else {
//			c.Tunnels["default"] = createDefaultTunnel(opts)
//		}
//
//	case "start":
//		if len(opts.args) == 0 {
//			log.Warn("No tunnels specified to start")
//		}
//
//		validTunnels := make(map[string]bool)
//		for _, name := range opts.args {
//			if _, exists := c.Tunnels[name]; !exists {
//				log.Warn("Undefined tunnel: %s", name)
//			}
//			validTunnels[name] = true
//		}
//
//		for name := range c.Tunnels {
//			if !validTunnels[name] {
//				delete(c.Tunnels, name)
//			}
//		}
//	}
//}
//
//func (c *Configuration) Validate() error {
//	if c.AuthToken == "" {
//		return errors.New("authentication token required")
//	}
//
//	if _, err := normalizeAddress(c.ServerAddr, "server_addr"); err != nil {
//		return err
//	}
//
//	if c.InspectAddr != "disabled" {
//		if _, err := normalizeAddress(c.InspectAddr, "inspect_addr"); err != nil {
//			return err
//		}
//	}
//
//	if c.HttpProxy != "" {
//		if _, err := validateProxyURL(c.HttpProxy); err != nil {
//			return err
//		}
//	}
//
//	for name, t := range c.Tunnels {
//		if len(t.Protocols) == 0 {
//			return fmt.Errorf("tunnel %s: no protocols defined", name)
//		}
//
//		for proto, addr := range t.Protocols {
//			if err := validateProtocol(proto); err != nil {
//				return fmt.Errorf("tunnel %s: %w", name, err)
//			}
//
//			if _, err := normalizeAddress(addr, proto); err != nil {
//				return fmt.Errorf("tunnel %s: %w", name, err)
//			}
//		}
//
//		if t.RemotePort < 0 || t.RemotePort > 65535 {
//			return fmt.Errorf("tunnel %s: invalid port %d", name, t.RemotePort)
//		}
//	}
//
//	return nil
//}
//
//func normalizeAddress(addr, context string) (string, error) {
//	if addr == "" {
//		return "", fmt.Errorf("%s: empty address", context)
//	}
//
//	if _, err := strconv.Atoi(addr); err == nil {
//		addr = ":" + addr
//	}
//
//	host, port, err := net.SplitHostPort(addr)
//	if err != nil {
//		if strings.Contains(addr, "]") {
//			return "", fmt.Errorf("%s: IPv6 address requires port", context)
//		}
//		addr += ":80"
//		host, port, err = net.SplitHostPort(addr)
//		if err != nil {
//			return "", fmt.Errorf("%s: %w", context, err)
//		}
//	}
//
//	if host == "" {
//		host = "127.0.0.1"
//	}
//	return net.JoinHostPort(host, port), nil
//}
//
//func validateProtocol(proto string) error {
//	allowed := map[string]bool{
//		"http":       true,
//		"https":      true,
//		"http+https": true,
//		"tcp":        true,
//		"udp":        true,
//	}
//	if !allowed[proto] {
//		return fmt.Errorf("invalid protocol: %s", proto)
//	}
//	return nil
//}
//
//func validateProxyURL(rawURL string) (*url.URL, error) {
//	u, err := url.Parse(rawURL)
//	if err != nil {
//		return nil, fmt.Errorf("invalid proxy URL: %w", err)
//	}
//
//	switch u.Scheme {
//	case "http", "https":
//	default:
//		return nil, fmt.Errorf("unsupported proxy scheme: %s", u.Scheme)
//	}
//
//	return u, nil
//}
//
//func defaultPath() string {
//	home, err := os.UserHomeDir()
//	if err != nil {
//		log.Warn("无法获取主目录: %v", err)
//		return ".tikrok"
//	}
//	return path.Join(home, ".tikrok.yml")
//}
//
//func SaveAuthToken(path, token string) error {
//	c := &Configuration{
//		Version:   configVersion,
//		AuthToken: token,
//	}
//
//	data, err := yaml.Marshal(c)
//	if err != nil {
//		return fmt.Errorf("序列化失败: %w", err)
//	}
//
//	if err := os.WriteFile(path, data, 0600); err != nil {
//		return fmt.Errorf("写入失败: %w", err)
//	}
//	return nil
//}
//
//func isDomainName(s string) bool {
//	if len(s) > 253 {
//		return false
//	}
//	labels := strings.Split(s, ".")
//	if len(labels) < 2 {
//		return false
//	}
//	for _, label := range labels {
//		if !regexp.MustCompile(`^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$`).MatchString(label) {
//			return false
//		}
//	}
//	return true
//}
//
//func createDefaultTunnel(opts *Options) *TunnelConfiguration {
//	t := &TunnelConfiguration{
//		Subdomain: opts.subdomain,
//		Hostname:  opts.hostname,
//		HttpAuth:  opts.httpauth,
//		Protocols: make(map[string]string),
//	}
//
//	for _, proto := range strings.Split(opts.protocol, "+") {
//		if addr, err := normalizeAddress(opts.args[0], ""); err == nil {
//			t.Protocols[proto] = addr
//		}
//	}
//
//	return t
//}
//
//func mergeTunnelConfig(existing *TunnelConfiguration, opts *Options) {
//	if opts.subdomain != "" {
//		existing.Subdomain = opts.subdomain
//	}
//	if opts.hostname != "" {
//		existing.Hostname = opts.hostname
//	}
//	if opts.httpauth != "" {
//		existing.HttpAuth = opts.httpauth
//	}
//
//	for _, proto := range strings.Split(opts.protocol, "+") {
//		if addr, err := normalizeAddress(opts.args[0], ""); err == nil {
//			existing.Protocols[proto] = addr
//		}
//	}
//}
